#include "minesweeper.h"
#include "ui_minesweeper.h"

MainGame::MainGame(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainGame), about(this), custom(this), record(this), userInfo(this)
{
	ui->setupUi(this);
	userInfo.setUser(&user);
	AIThread.link(this);
}

MainGame::~MainGame()
{
	AIThread.stop();
	delete ui;
}

void MainGame::loginSlot(User user)
{
	this->user = user;
	this->setEasyLevel();
	this->show();
}

void MainGame::logout()
{
	this->hide();
	emit logoutSignal();
}

void MainGame::loadImage()
{
	image.block.load(":/Images/block.png");
	image.cover.load(":/Images/cover.png");
	image.error.load(":/Images/error.png");
	image.mine.load(":/Images/mine.png");
	image.mineError.load(":/Images/mine_error.png");
	image.flag.load(":/Images/flag.png");

	static char path[INFO_MAX_LEN];

	for (int i = 0; i < NUMBER_COUNT; i++)
	{
		sprintf(path, ":/Images/number_%d.png", i + 1);
		image.number[i].load(path);
	}
}

void MainGame::loadAudio()
{
	audio.click.setMedia(QUrl("qrc:/Audio/click.mp3"));
	audio.lose.setMedia(QUrl("qrc:/Audio/lose.mp3"));
	audio.win.setMedia(QUrl("qrc:/Audio/win.mp3"));
}

void MainGame::setEasyLevel()
{
	level = EASY_LEVEL;
	tableRows = TABLE_ROWS_EASY;
	tableCols = TABLE_COLS_EASY;
	mineInitCount = MINE_INIT_COUNT_EASY;
	resizeWindow();
	initGame();
}

void MainGame::setNormalLevel()
{
	level = NORMAL_LEVEL;
	tableRows = TABLE_ROWS_NORMAL;
	tableCols = TABLE_COLS_NORMAL;
	mineInitCount = MINE_INIT_COUNT_NORMAL;
	resizeWindow();
	initGame();
}

void MainGame::setHighLevel()
{
	level = HIGH_LEVEL;
	tableRows = TABLE_ROWS_HIGH;
	tableCols = TABLE_COLS_HIGH;
	mineInitCount = MINE_INIT_COUNT_HIGH;
	resizeWindow();
	initGame();
}

void MainGame::setCustomLevel()
{
	custom.displayDialog();

	if (custom.getIfNeedSet())
	{
		level = CUSTOM_LEVEL;
		tableRows = custom.getInputRows();
		tableCols = custom.getInputCols();
		mineInitCount = custom.getInputMineCount();
		resizeWindow();
		initGame();
	}
}

void MainGame::resizeWindow()
{
	//  根据游戏界面的行数和列数确定游戏界面和窗口的大小

	tableWidth = tableRows * BLOCK_WIDTH;
	tableHeight = tableCols * BLOCK_WIDTH;
	screenWidth = tableWidth + MARGIN_X * 2;
	screenHeight = tableHeight + MARGIN_X + MARGIN_Y;

	setFixedSize(screenWidth, screenHeight);

	//  将窗口移动到可以使窗口居中的位置

	setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, size(), QApplication::desktop()->availableGeometry()));
}

void MainGame::mainInterval()
{
	if (status == PLAYING)
	{
		MainGame::gameoverWin();
		MainGame::gameoverLose();
		MainGame::update();
	}
	QWidget::update();
}

void MainGame::clockCallback()
{
	if (status == PLAYING) { timeSec += 1; }
}

void MainGame::setInterval()
{
	timer.interval.setInterval(1000 / GAME_FPS);
	timer.clock.setInterval(CLOCK_INTERVAL);
}

void MainGame::connectTimer()
{
	connect(&timer.interval, &QTimer::timeout, this, &MainGame::mainInterval);
	connect(&timer.clock, &QTimer::timeout, this, &MainGame::clockCallback);
}

void MainGame::connectAction()
{
	connect(ui->actionEasy, &QAction::triggered, this, &MainGame::setEasyLevel);
	connect(ui->actionNormal, &QAction::triggered, this, &MainGame::setNormalLevel);
	connect(ui->actionHigh, &QAction::triggered, this, &MainGame::setHighLevel);
	connect(ui->actionCustom, &QAction::triggered, this, &MainGame::setCustomLevel);
	connect(ui->actionPause, &QAction::triggered, this, &MainGame::setPauseStatus);
	connect(ui->actionRestart, &QAction::triggered, this, &MainGame::initGame);
	connect(ui->actionRecord, &QAction::triggered, &record, &RecordDialog::displayDialog);
	connect(ui->actionAbout, &QAction::triggered, &about, &AboutDialog::exec);
	connect(ui->actionUser, &QAction::triggered, &userInfo, &UserInfoDialog::displayDialog);
	connect(ui->actionLogout, &QAction::triggered, this, &MainGame::logout);
}

void MainGame::startTimer()
{
	timer.interval.start();
	timer.clock.start();
}

void MainGame::initGame()
{
	for (int i = 0; i < tableRows; i++)
	{
		for (int j = 0; j < tableCols; j++)
		{
			block[i][j].initData();
		}
	}
	status = PLAYING;
	flagCount = mineInitCount;
	nullCount = 0;
	timeSec = 0;
	isCracked = false;
	ifHaveCracked = false;
	isInAIMode = false;
	ifHaveAIMode = false;
	AIThread.terminate();
	addMine();
	addNumber();
}

void MainGame::initColor()
{
	color.white.setRgb(COLOR_WHITE);
	color.gray.setRgb(COLOR_GRAY);
	color.black.setRgb(COLOR_BLACK);
}

void MainGame::addMine()
{
	static QVector <int> numberList;
	static Point targetPoint;

	mineList.clear();

	for (int i = 0; i < TABLE_ROWS_MAX * TABLE_COLS_MAX; i++)
	{
		//  从左到右给游戏界面上的位置进行编号并依次写入列表中

		numberList.append(i);
	}
	for (int i = 0; i < tableRows * tableCols; i++)
	{
		//  随机交换列表中界面大小数量的元素以实现随机打乱列表

		qSwap(numberList[i], numberList[rand() % (tableRows * tableCols)]);
	}
	for (int i = 0; i < mineInitCount; i++)
	{
		//  选取打乱后列表的前 mineInitCount 个元素以实现随机选取位置

		targetPoint.x = numberList[i] % tableRows;
		targetPoint.y = numberList[i] / tableRows;

		//  将编号转化为位置坐标并将该点设置为地雷

		block[targetPoint.x][targetPoint.y].addMine();
		mineList.append(targetPoint);
	}
	numberList.clear();
}

void MainGame::addNumber()
{
	for (int i = 0; i < mineInitCount; i++)
	{
		//  遍历所有地雷的位置

		for (int sideX = -1; sideX <= 1; sideX++)
		{
			for (int sideY = -1; sideY <= 1; sideY++)
			{
				//  遍历地雷相邻的所有位置

				int x = mineList[i].x + sideX;
				int y = mineList[i].y + sideY;

				if (x >= 0 && x < tableRows && y >= 0 && y < tableCols && !block[x][y].getIsMine())
				{
					//  如果这个位置在界面内且没有地雷，则在此位置添加数字

					block[x][y].addNumber();
				}
			}
		}
	}
}

void MainGame::setPauseStatus()
{
	if (status == PLAYING && !isCracked && !isInAIMode)
	{
		status = PAUSE;
	}
}

void MainGame::gameoverWin()
{
	for (int x = 0; x < tableRows; x++)
	{
		for (int y = 0; y < tableCols; y++)
		{
			//  遍历整个游戏界面

			if (!block[x][y].getIsMine() && block[x][y].getIsCovered())
			{
				//  如果该位置不是地雷却还没有被打开，则未获胜，直接退出

				return;
			}
		}
	}
	//  如果所有不是地雷的位置均被打开，则判定胜利

	audio.win.stop();
	audio.win.play();
	status = WIN;

	//  如果未处于自定义难度且未开启过作弊模式或自动模式，则可以记录成绩

	if (level != CUSTOM_LEVEL && !ifHaveCracked && !ifHaveAIMode)
	{
		user.addRecord(timeSec, level);
	}
}

void MainGame::gameoverLose()
{
	for (int x = 0; x < tableRows; x++)
	{
		for (int y = 0; y < tableCols; y++)
		{
			if (block[x][y].getIsTouched())
			{
				//  当踩到地雷，游戏结束时

				audio.lose.stop();
				audio.lose.play();

				for (int x = 0; x < tableRows; x++)
				{
					for (int y = 0; y < tableCols; y++)
					{
						//  遍历游戏界面的所有位置

						block[x][y].loseOperation();
					}
				}
				status = OVER;
				return;
			}
		}
	}
}

void MainGame::update()
{
	//  这个函数用于自动将空白位置相邻的位置打开

	static QVector <Point> nullList;
	static QVector <Operation> operationList;

	//  先获取当前游戏界面上被打开空白位置的信息

	for (int x = 0; x < tableRows; x++)
	{
		for (int y = 0; y < tableCols; y++)
		{
			if (block[x][y].getIsNull() && !block[x][y].getIsCovered())
			{
				//  将空白位置的数据写入相应列表中

				nullList.append({ x, y });
			}
		}
	}

	//  如果空白位置的数量有所增加，则需要对所有空白位置的相邻位置进行处理

	if (nullList.size() > nullCount)
	{
		isOpenFinish = false;

		for (int i = 0; i < nullList.size(); i++)
		{
			//  遍历所有的空白位置

			for (int sideX = -1; sideX <= 1; sideX++)
			{
				for (int sideY = -1; sideY <= 1; sideY++)
				{
					//  遍历此位置相邻的位置

					int x = nullList[i].x + sideX;
					int y = nullList[i].y + sideY;

					if (x >= 0 && x < tableRows && y >= 0 && y < tableCols)
					{
						//  如果这个位置在界面内没有被标记，则自动点击此位置

						block[x][y].leftClick();
					}
				}
			}
		}
		nullCount = nullList.size();
	}
	else { isOpenFinish = true; }

	nullList.clear();
}

void MainGame::mousePressEvent(QMouseEvent* event)
{
	int clicked = event->button();

	if (clicked && !isCracked && !isInAIMode)
	{
		if (status == PLAYING)
		{
			//  获取当前鼠标的位置信息

			mouse = event->pos();

			if (mouse.x() >= MARGIN_X && mouse.x() <= screenWidth - MARGIN_X && mouse.y() >= MARGIN_Y && mouse.y() <= screenHeight - MARGIN_X)
			{
				//  如果鼠标在界面内，则利用整型除运算向下取整的性质计算出鼠标所在的方格位置

				int x = ((mouse.x() - MARGIN_X) / BLOCK_WIDTH);
				int y = ((mouse.y() - MARGIN_Y) / BLOCK_WIDTH);

				if (block[x][y].getIsCovered())
				{
					if (clicked == Qt::LeftButton)
					{
						//  如果左键点击这个位置，则执行相关操作

						block[x][y].leftClick();

						if (!block[x][y].getIsTouched())
						{
							audio.click.stop();
							audio.click.play();
						}
					}
					else if (clicked == Qt::RightButton)
					{
						//  如果右键点击这个位置，则执行相关操作

						audio.click.stop();
						audio.click.play();

						block[x][y].rightClick(flagCount);
					}
				}
			}
		}
		else
		{
			audio.click.stop();
			audio.click.play();

			if (status == PAUSE)
			{
				status = PLAYING;
			}
			else { initGame(); }
		}
	}
}

void MainGame::keyPressEvent(QKeyEvent *event)
{
	if (event->key() == Qt::Key_Z && status == PLAYING)
	{
		isCracked = true;
		ifHaveCracked = true;
	}
	if (event->key() == Qt::Key_A && status == PLAYING && !isCracked)
	{
		if (!isInAIMode)
		{
			isInAIMode = true;
			ifHaveAIMode = true;
			AIThread.start();
		}
		else
		{
			isInAIMode = false;
			AIThread.stop();
		}
	}
	if (event->key() == Qt::Key_R) { initGame(); }
	if (event->key() == Qt::Key_P) { setPauseStatus(); }
}

void MainGame::keyReleaseEvent(QKeyEvent *event)
{
	if (event->key() == Qt::Key_Z && status == PLAYING)
	{
		isCracked = false;
	}
}

void MainGame::displayBackground(QPainter& painter)
{
	//  根据窗口的参数绘制游戏界面背景

	static QRect rect;

	painter.fillRect(0, 0, screenWidth, screenHeight, QBrush(color.white));

	rect.setX(MARGIN_X - BORDER);
	rect.setY(MARGIN_Y - BORDER);
	rect.setWidth(tableWidth + 2 * BORDER);
	rect.setHeight(tableHeight + 2 * BORDER);

	painter.fillRect(rect, QBrush(color.black));

	rect.setX(MARGIN_X - BLOCK_BORDER);
	rect.setY(MARGIN_Y - BLOCK_BORDER);
	rect.setWidth(tableWidth + 2 * BLOCK_BORDER);
	rect.setHeight(tableHeight + 2 * BLOCK_BORDER);

	painter.fillRect(rect, QBrush(color.gray));
}

void MainGame::displayBlock(QPainter& painter)
{
	//  根据游戏数据绘制游戏界面的方块

	for (int i = 0; i < tableRows; i++)
	{
		for (int j = 0; j < tableCols; j++)
		{
			//  根据位置信息计算出方块在窗口中的实际位置

			int x = MARGIN_X + i * BLOCK_WIDTH;
			int y = MARGIN_Y + j * BLOCK_WIDTH;

			if ((block[i][j].getIsCovered() && !isCracked) || status == PAUSE)
			{
				//  当游戏处于暂停状态时，遮住游戏界面中的所有的内容以维护游戏的公平性

				painter.drawPixmap(x, y, BLOCK_WIDTH, BLOCK_WIDTH, image.cover);

				if (block[i][j].getIsError() && status != PAUSE)
				{
					painter.drawPixmap(x, y, BLOCK_WIDTH, BLOCK_WIDTH, image.error);
				}
				else if (block[i][j].getIsMarked() && status != PAUSE)
				{
					painter.drawPixmap(x, y, BLOCK_WIDTH, BLOCK_WIDTH, image.flag);
				}
			}
			else
			{
				painter.drawPixmap(x, y, BLOCK_WIDTH, BLOCK_WIDTH, image.block);

				if (block[i][j].getIsMine())
				{
					painter.drawPixmap(x, y, BLOCK_WIDTH, BLOCK_WIDTH, (block[i][j].getIsTouched()) ? image.mineError : image.mine);
				}
				else if (block[i][j].getIsNumber())
				{
					painter.drawPixmap(x, y, BLOCK_WIDTH, BLOCK_WIDTH, image.number[block[i][j].getNumber() - 1]);
				}
			}
		}
	}
}

void MainGame::displayInfo(QPainter& painter)
{
	//  绘制游戏界面上方的提示信息

	static char text[INFO_MAX_LEN];

	if (!isInAIMode)
	{
		switch (status)
		{
			case PAUSE: snprintf(text, INFO_MAX_LEN, "TIME: %d (PAUSE)", timeSec); break;
			default: snprintf(text, INFO_MAX_LEN, "TIME: %d", timeSec); break;
		}
	}
	else { snprintf(text, INFO_MAX_LEN, "TIME: %d (AI)", timeSec); }

	painter.drawText(MARGIN_X, INFO_POSITION, text);

	switch (status)
	{
		case OVER: snprintf(text, INFO_MAX_LEN, "BOOM!"); break;
		case WIN: snprintf(text, INFO_MAX_LEN, "Success!"); break;
		default: snprintf(text, INFO_MAX_LEN, "MINES: %d", flagCount); break;
	}
	painter.drawText(screenWidth - INFO_WIDTH, INFO_POSITION, text);
}

void MainGame::paintEvent(QPaintEvent*)
{
	QPainter painter(this);

	displayBackground(painter);
	displayBlock(painter);
	displayInfo(painter);
}

void AIMainThread::link(MainGame *game)
{
	this->game = game;
}

void AIMainThread::stop()
{
	this->terminate();
	this->wait();
}

void AIMainThread::run()
{
	static QVector <Operation> operationList;

	while (true)
	{
		if (game->isInAIMode && game->isOpenFinish && game->status == PLAYING)
		{
			operationList = game->AIMode.run(game->block, game->tableRows, game->tableCols, game->flagCount);

			for (int i = 0; i < operationList.size(); i++)
			{
				switch (operationList[i].click)
				{
					case LEFT_CLICK: game->block[operationList[i].x][operationList[i].y].leftClick(); break;
					case RIGHT_CLICK: game->block[operationList[i].x][operationList[i].y].rightClick(game->flagCount); break;
				}
			}
			game->isOpenFinish = false;
		}
		msleep(AI_THREAD_INTERVAL);
	}
}
